package com.ts.api.aspect;

import com.gitee.apanlh.util.base.Eq;
import com.gitee.apanlh.util.base.StringUtils;
import com.gitee.apanlh.util.dataformat.JsonUtils;
import com.gitee.apanlh.util.date.DateUtils;
import com.gitee.apanlh.util.id.IdUtils;
import com.gitee.apanlh.util.net.addr.IpUtils;
import com.gitee.apanlh.util.reflection.ClassConvertUtils;
import com.gitee.apanlh.util.valid.ValidParam;
import com.gitee.apanlh.web.model.vo.RequestVO;
import com.gitee.apanlh.web.model.vo.ResultVO;
import com.gitee.apanlh.web.util.ServletUtils;
import com.ts.api.aspect.handler.ApiLogAspectHandler;
import com.ts.api.common.constant.api.ApiEnum;
import com.ts.api.common.context.ApiContext;
import com.ts.api.common.entity.ApiParameter;
import com.ts.api.common.entity.MsgFormat;
import com.ts.api.exp.ApiMsgException;
import com.ts.api.module.api.entity.po.ApiLogPO;
import com.ts.api.task.log.ApiLogExecute;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

/**
 */
@Component
@Aspect
@Slf4j
public class ApiLogAspect {

    @Autowired
    private ApiLogExecute apiLogExecute;
    @Autowired
    private ApiLogAspectHandler apiLogAspectHandler;

    @Before("execution(*  com.ts.api.module.api.controller.ApiMsgController.*(..))")
    public void before(JoinPoint joinPoint) {
        ApiLogPO logPO = new ApiLogPO();
        logPO.setCreateTime(DateUtils.currentTime());
        ApiContext.REQ_ID.set(IdUtils.snowFlakeIdStr(2L, 1L));
        ApiContext.LOG_OBJECT.set(logPO);
        ApiContext.TIME_TIME.set(System.currentTimeMillis());
        ApiContext.PARAMETER.set(ApiParameter.create());
    }

    /**
     *  拦截异常操作
     *
     *  @param joinPoint 切点
     *  @param e 异常
     */
    @AfterThrowing(value = "execution(*  com.ts.api.module.api.controller.ApiMsgController.*(..))", throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {
        handleLog(joinPoint, e, null);
    }

    /**
     *  处理完请求后执行
     *
     *  @param joinPoint 切点
     */
    @AfterReturning(pointcut = "execution(*  com.ts.api.module.api.controller.ApiMsgController.*(..))", returning = "result")
    public void doAfterReturning(JoinPoint joinPoint, Object result) {
        handleLog(joinPoint, null, result);
    }

    protected void handleLog(final JoinPoint joinPoint, final Throwable e, Object jsonResult) {
        try {
            ApiLogPO logPO = ApiContext.LOG_OBJECT.get();

            Object[] args = joinPoint.getArgs();
            RequestVO requestVO = null;
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                if (Eq.object(arg.getClass(), RequestVO.class)) {
                    requestVO = ((RequestVO) args[i]);
                }
            }

            // *========数据库日志=========*//
            logPO.setEventStatus(1);
            logPO.setEventContent("返回成功");
            logPO.setIp(IpUtils.getIpAddr(ServletUtils.getRequest()));
            logPO.setApiAccessProviderType(1);

            if (ValidParam.isEmpty(logPO.getEventType())) {
                logPO.setEventType(ApiContext.SERVICE_CODE.get());
            }

            if (e != null) {
                //  默认设置失败
                ResultVO<Object> resultVO = ResultVO.fail();
                logPO.setEventStatus(0);

                if (e instanceof ApiMsgException) {
                    ApiMsgException apiExp = (ApiMsgException) e;
                    MsgFormat msgFormat = apiExp.getMsgFormat();
                    ApiEnum apiEnum = apiExp.getApiEnum();
                    Integer code;
                    String msg;

                    if (apiEnum == null) {
                        code = msgFormat.getCode();
                        msg = msgFormat.getMsg();
                    } else {
                        code = apiEnum.getCode();
                        msg = apiEnum.getMsg();
                    }

                    logPO.setEventCode(String.valueOf(code));
                    logPO.setEventMsg(msg);
                    logPO.setEventContent(e.getMessage());
                    resultVO.setCode(code);
                    resultVO.setMsg(msg);

                    logPO.setEventCode(ClassConvertUtils.toStr(code));
                    logPO.setEventMsg(msg);
                } else {
                    StringBuilder sb = StringUtils.createBuilder(e).append("\n");
                    StackTraceElement[] stackTrace = e.getStackTrace();
                    for (StackTraceElement stackTraceElement : stackTrace) {
                        sb.append("\t" + stackTraceElement).append("\n");
                    }
                    logPO.setEventContent(sb.toString());

                    resultVO.setCode(500);
                    resultVO.setMsg("请求处理失败");

                    logPO.setEventCode("500");
                    logPO.setEventMsg("请求处理异常");
                }
                logPO.setRespData(JsonUtils.toJson(resultVO));
            } else {
                logPO.setEventCode(ApiEnum.API_SIGN_SUC.getCode().toString());
                logPO.setEventMsg(ApiEnum.API_SIGN_SUC.getMsg());
                logPO.setRespData(JsonUtils.toJson(jsonResult));
            }

            logPO.setReqUrl(ServletUtils.getRequest().getRequestURI());
            logPO.setReqId(ClassConvertUtils.toLong(ApiContext.REQ_ID.get()));
            logPO.setReqData(requestVO.getBody());
            logPO.setReqHeader(requestVO.getHeader().toString());

            //  请求参数日志记录
            apiLogAspectHandler.handlerRequestParameter(logPO);
            //  清理并发异常日志
            apiLogAspectHandler.clearConcurrentError(logPO);

            // 设置消耗时间
            logPO.setTaskTime(System.currentTimeMillis() - ApiContext.TIME_TIME.get());
            // 保存数据库
            apiLogExecute.execute(logPO);
        } finally {
            removeApiContext();
        }
    }

    /**
     *  移除上下文
     */
    public static void removeApiContext() {
        ApiContext.REQ_ID.remove();
        ApiContext.LOG_OBJECT.remove();
        ApiContext.TIME_TIME.remove();
        ApiContext.CLIENT_ID.remove();
        ApiContext.REQ_PARAMS.remove();
        ApiContext.DECRYPT_DATA.remove();
        ApiContext.SERVICE_CODE.remove();
        ApiContext.PARAMETER.remove();
    }
}
